using System;
using System.Web.UI;
using System.Text.RegularExpressions;
using WPS.Libraries.CssDom;
using System.Configuration;

/*
    CssDom Library
    Copyright (C) 2007-2011  Ferenc Veres

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

namespace WPS.Modules.StyleLibrary
{
	/// <summary>
	/// Baseclass for style library ascx.cs files.
	/// </summary>
	public abstract class StyleLibraryBasePage : System.Web.UI.Page
	{
		/// <summary>
		/// Must be set by implementors to their integration interface in their Page_Load.
		/// </summary>
		protected IStyleLibraryIntegration CmsIntegration;

		#region Template controls

		/// <summary>
		/// The loaded style's original name.
		/// </summary>
		protected string OriginalName;

		/// <summary>
		/// The current style's name as edited on the GUI textbox.
		/// </summary>
		protected string NewName;

		/// <summary>
		/// The complete style definition from the client side designer.
		/// </summary>
		protected string CssOutput;

		/// <summary>
		/// Javascript array generated with the loaded style rules as input to the client side designer.
		/// </summary>
		protected string LoadedStylesJS;

		/// <summary>
		/// If overview displayed only one style group, this preserves that value.
		/// </summary>
		protected string OverviewStyles = "";

		/// <summary>
		/// Used by save routine to remove empty secondary rules of table styles and other optional empty rules.
		/// </summary>
		protected bool RemoveEmptyRules = false;

		#endregion

		#region Private variables

		#endregion

		#region Page events

		protected override void OnPreInit(EventArgs e)
		{
			base.OnPreInit(e);
		}

		/// <summary>
		/// Page load event.
		/// </summary>
		/// <param name="sender">Sender</param>
		/// <param name="e">Event args</param>
		protected void Page_Load(object sender, System.EventArgs e)
		{
			if(!this.CmsIntegration.CheckAccess())
			{
				Response.Redirect(this.CmsIntegration.GetLoginPage(), true);
			}

			if(!Page.IsPostBack)
			{
				// Store which styles were shown on the Overview
				if(!String.IsNullOrEmpty(Request.QueryString["styles"]))
				{
					this.OverviewStyles = Request.QueryString["styles"];
				}

				// Decide to do edit/new/editasnew/delete.
				if(!String.IsNullOrEmpty(Request.QueryString["class"]))
				{
					this.OriginalName = Request.QueryString["class"];

					// Delete stlye?
					if(Request.QueryString["delete"] == "1")
					{
						DeleteStyle(this.OriginalName);
						SendPageMessage("{#stylelibrary_dlg.style_deleted}", false);
						string url = "Overview.aspx";
						if(this.OverviewStyles != "")
						{
							url += (url.Contains("?") ? "&" : "?");
							url += "styles=" + this.OverviewStyles;
						}
						Response.Redirect(url);
					}

					// Load style
					if(Request.QueryString["asnew"] == "1")
					{
						// Edit as new
						this.NewName = "";
						LoadAsNew(this.OriginalName, false); // Rename to "as new" code while loading
					}
					else if(Request.QueryString["examples"] == "1")
					{
						// Copy from examples.css
						this.NewName = "";
						LoadAsNew(this.OriginalName, true);
					}
					else
					{
						// Edit existing
						this.NewName = this.OriginalName;
						LoadStyle(this.NewName, this.NewName, false); // No rename needed
					}
				}
				else
				{
					// New style
					this.NewName = "";
					this.OriginalName = "StyleCreateNEW";
					CreateStyle("StyleCreateNEW");
				}
			}
			else
			{
				// Postback
				this.NewName = "";
				if(!String.IsNullOrEmpty(Request.Form["txtNewName"]))
				{
					this.NewName = Request.Form["txtNewName"].Trim();
				}
				this.OriginalName = Request.Form["hdnOriginalName"].Trim();
				this.CssOutput = Request.Form["hdnCssOutput"].Trim();
				this.OverviewStyles = Request.Form["hdnOverviewStyles"].Trim();

				ReloadStyle(this.OriginalName, this.CssOutput);
			}
		}

		/// <summary>
		/// Prerender page event.
		/// </summary>
		/// <param name="sender">Sender</param>
		/// <param name="e">Event args</param>
		protected void Page_PreRender(object sender, System.EventArgs e)
		{
			DataBind();
		}

		#endregion

		#region Button events

		/// <summary>
		/// Load CSS file, replace rules and save.
		/// </summary>
		/// <param name="sender">Sender</param>
		/// <param name="e">Event args</param>
		protected virtual void btnSave_Click(object sender, EventArgs e)
		{
			if(ValidateForm())
			{
				string cssFileName = GetCssFilePath(false);
				StyleSheetManager.UpdateCssFile(cssFileName, this.CssOutput, this.RemoveEmptyRules, GetRenames(this.NewName));

				this.CmsIntegration.IncrementEditableCssRevision();

				SendPageMessage("{#stylelibrary_dlg.style_saved}", false);
				Response.Redirect(AppendUrl("Overview.aspx"));
			}
		}

		/// <summary>
		/// Cancel editing and return to the overview.
		/// </summary>
		/// <param name="sender">Sender.</param>
		/// <param name="e">Event args.</param>
		protected virtual void btnCancel_Click(object sender, EventArgs e)
		{
			Response.Redirect(AppendUrl("Overview.aspx"));
		}

		/// <summary>
		/// Add layout override and overview styles selection to a redirection url.
		/// </summary>
		/// <param name="url">Original URL.</param>
		/// <returns>New URL with appended styles and layout query values if there are any.</returns>
		protected virtual string AppendUrl(string url)
		{
			if(this.OverviewStyles != "")
			{
				url += (url.Contains("?") ? "&" : "?");
				url += "styles=" + this.OverviewStyles;
			}
			return url;
		}
		#endregion

		#region CSS loading, creating, deleting

		/// <summary>
		/// Load wyswiyg.css and fill values to form controls.
		/// </summary>
		/// <param name="styleName">Which table class to find and load.</param>
		/// <param name="renamedStyle">Rename the style while loading.</param>
		protected virtual void LoadStyle(string styleName, string renamedStyle, bool fromExamples)
		{
			StyleRuleSearch[] rules = GetRules(styleName);

			CSSStyleSheet css = new CSSStyleSheet();

			css.Load(GetCssFilePath(fromExamples)); //(it is safe to ignore whether load failed)

			// Find or create our style's related CSS rules. 
			StyleSheetManager.SearchStyleRules(css, ref rules, true);
			StyleSheetManager.RenameRules(ref rules, GetRenames(renamedStyle)); // may rename to "as new" name

			this.LoadedStylesJS = StyleSheetManager.CreateJSArray(rules);
		}

		/// <summary>
		/// Load wyswiyg.css and fill values to form controls.
		/// </summary>
		/// <param name="styleName">Which table class to find and load.</param>
		/// <param name="renamedStyle">Rename the style while loading.</param>
		protected virtual void LoadAsNew(string styleName, bool fromExamples)
		{
			LoadStyle(styleName, "StyleCreateASNEW", fromExamples);
			this.OriginalName = "StyleCreateASNEW";
		}

		/// <summary>
		/// Create the Javascript array for new style.
		/// </summary>
		/// <param name="styleName">New stlye's temporary name.</param>
		protected virtual void CreateStyle(string styleName)
		{
			StyleRuleSearch[] rules = GetRules(styleName);
			this.LoadedStylesJS = StyleSheetManager.CreateJSArray(rules);
		}

		/// <summary>
		/// Recreate the javascript array from the posted previous edited styles (hdnCssOutput).
		/// </summary>
		/// <param name="styleName">Style name in the editedStyles string for Rule matching.</param>
		/// <param name="editedStyles">CSS contents to parse and put in the array.</param>
		protected virtual void ReloadStyle(string styleName, string editedStyles)
		{
			CSSStyleSheet css = new CSSStyleSheet();
			css.LoadString(editedStyles);
			StyleRuleSearch[] rules = GetRules(styleName);
			StyleSheetManager.SearchStyleRules(css, ref rules, true);
			this.LoadedStylesJS = StyleSheetManager.CreateJSArray(rules);
		}

		/// <summary>
		/// Delete the selected style's all rules.
		/// </summary>
		/// <param name="styleName">Which style to delete.</param>
		protected virtual void DeleteStyle(string styleName)
		{
			// Load WYSIWYG.CSS
			string cssFileName = GetCssFilePath(false);
			CSSStyleSheet css = new CSSStyleSheet();

			if(css.Load(cssFileName))
			{
				// Find our related CSS rules and delete them.
				StyleRuleSearch[] rules = GetRules(styleName);
				StyleSheetManager.DeleteStyleRules(css, ref rules);
				css.Save(cssFileName);
			}
		}

		/// <summary>
		/// Get the full path to WYSIWYG.css. May return path to not yet existing file or directory.
		/// </summary>
		/// <param name="examplesCss">Return the Style Library examples.css path (should never write to that!) instead the output CSS file path.</param>
		/// <returns>Path and filename to the Style Library CSS file's location.</returns>
		protected string GetCssFilePath(bool examplesCss)
		{
			if(examplesCss)
			{
				return Server.MapPath(this.CmsIntegration.GetExampleCssWebPath());
			}
			else
			{
				return Server.MapPath(this.CmsIntegration.GetEditableCssWebPath());
			}
		}

		#endregion

		#region Form validation

		/// <summary>
		/// Validate form data before saving.
		/// </summary>
		/// <returns>True on valid form, false and sends page message on error.</returns>
		protected virtual bool ValidateForm()
		{
			bool valid = true;

			if(this.NewName == "")
			{
				SendPageMessage("{#stylelibrary_dlg.name_missing}", true);
				valid = false;
			}

			Regex nameRx = new Regex("^[a-z]+[a-z0-9_-]+$", RegexOptions.IgnoreCase);
			if(valid && !nameRx.Match(this.NewName).Success)
			{
				SendPageMessage("{#stylelibrary_dlg.spec_chars}", true);
				valid = false;
			}

			// New style. Check name conflict.
			if(valid && this.OriginalName != this.NewName)
			{
				string cssFileName = GetCssFilePath(false);

				StyleNameList oldClasses = StyleSheetManager.LoadAllStyles(cssFileName);

				if(oldClasses.FindClass(this.NewName) != null)
				{
					SendPageMessage("{#stylelibrary_dlg.name_exists}", true);
					valid = false;
				}
			}

			return valid;
		}

		#endregion

		#region Virtual methods to be done by inheriting Style modules!

		/// <summary>
		/// Returns an array of StyleRuleSearch objects, with initialized for Table Designer related selectors.
		/// Uses this.StyleName for css class names.
		/// </summary>
		/// <param name="styleName">Style class name to use in the selectors.</param>
		/// <returns>StyleRuleSearch array with SelectorText filled for Table Designer CSS classes.</returns>
		protected virtual StyleRuleSearch[] GetRules(string styleName)
		{
			return null;
		}

		/// <summary>
		/// Return an array of selector strings with a new name.
		/// </summary>
		/// <param name="newName">New style name for selectors.</param>
		/// <returns>List of CSS selectors necessary for this style.</returns>
		protected virtual string[] GetRenames(string newName)
		{
			return null;
		}

		/// <summary>
		/// Method implemented in the designer ascx.cs to send messages to the PageMessageControl.
		/// </summary>
		/// <param name="message">Message text to send.</param>
		/// <param name="error">Message type, true is error, false is status.</param>
		protected virtual void SendPageMessage(string message, bool error)
		{
		}

		#endregion

		/// <summary>
		/// Escape the " to &quot; for use in HTML attributes.
		/// </summary>
		/// <param name="str">String to escape.</param>
		/// <returns>All " replaced to &quot;.</returns>
		public string quot(string str)
		{
			return str.Replace("\"", "&quot;");
		}

		/// <summary>
		/// Escape the ' to \' for use in javascript variables;
		/// </summary>
		/// <param name="str">String to escape.</param>
		/// <returns>All ' replaced to \'.</returns>
		public string esc(string str)
		{
			return str.Replace("'", "\\'");
		}

	}
}
